#include "parser.h"
#include "errors.h"
#include <set>
#include <map>
#include <iosfwd>
#include <string>

namespace CCompiler
{
	const int lowest_level = 2;
	std::set<TokenType> OpGroups[lowest_level + 1];
	std::map<std::string, TinyNameType> strToTNT;

	namespace ParserInit
	{
		class Initializer
		{
			Initializer();
		public:
			static Initializer initializer_;
		};
		Initializer Initializer::initializer_;
	}

	template<int priority>
	SynExpression *BasicParser::InternalParseExpression(Int2Type<priority>)
	{
		SynExpression *res = InternalParseExpression(Int2Type<priority + 1>());
		do 
		{
			Token token = tokenizer_.GetCurToken();
			if(OpGroups[priority].count(token.GetTokenType()))
			{
				tokenizer_.NextToken();
				res = new SynBinaryOp(token.GetText(), res, InternalParseExpression(Int2Type<priority + 1>()));
			}
			else break;
		} while (true);
		return res;
	}

	SynExpression *BasicParser::ParseIdentifier()
	{
		SynExpression* res = new SynVal(tokenizer_.GetCurToken().GetText());
		tokenizer_.NextToken();
		return res;
	}

	SynExpression *BasicParser2::ParseIdentifier()
	{
		SynExpression *res = 0;
		Token name = tokenizer_.GetCurToken();
		tokenizer_.NextToken();
		Token ntk = tokenizer_.GetCurToken();
		TokenType supToken = ntk.GetTokenType();
		TinyNameType vartype;
		if(!skipCheck_)
		{
			if(!tinySymbolTable.count(name.GetText()))
				throw Errors::SyntaxError("Name " + name.GetText() + " wasn't declared");
			else
				vartype = tinySymbolTable[name.GetText()];
		}
		else
			supToken = ttPlus;
		switch(supToken)
		{
			case ttOpenBrkt:
				if(vartype == ntFunction)
				{
					SynExpression *res = new SynVal(name.GetText()), *FunName;
					while(ntk.GetTokenType() == ttOpenBrkt)
					{
						FunName = res;
						ntk = tokenizer_.NextToken();
						if(ntk.GetTokenType() == ttCloseBrkt)
							res = new SynUnaryOp("()", FunName);
						else
						{
							res = ParseExpression();
							while(tokenizer_.GetCurToken().GetTokenType() == ttComma)
							{
								tokenizer_.NextToken();
								res = new SynBinaryOp(",", res, ParseExpression());
							}
							if(tokenizer_.GetCurToken().GetTokenType() != ttCloseBrkt)
								throw Errors::SyntaxError("Parenthesis mismatch");
							res = new SynBinaryOp("()", FunName, res);
						}
						ntk = tokenizer_.NextToken();
					}
					return res;
				}
				else
					throw Errors::SyntaxError(name.GetText() + " is not a function");
				break;
			case ttOpenSBrkt:
				if(vartype == ntArray)
				{
					res  = new SynVal(name.GetText());
					while(ntk.GetTokenType() == ttOpenSBrkt)
					{
						ntk = tokenizer_.NextToken();
						res = new SynBinaryOp("[]", res, ParseExpression());
						if(tokenizer_.GetCurToken().GetTokenType() != ttCloseSBrkt)
							throw Errors::SyntaxError("Square brackets mismatch");
						ntk = tokenizer_.NextToken();
					}
					return res;
				}
				else
					throw Errors::SyntaxError(name.GetText() + " is not an array");
				break;
			case ttDot:
				if(skipCheck_ || vartype == ntStruct)
				{
					skipCheck_ = true;
					res  = new SynVal(name.GetText());
					while(ntk.GetTokenType() == ttDot)
					{
						ntk = tokenizer_.NextToken();
						res = new SynBinaryOp(".", res, ParseIdentifier());
						ntk = tokenizer_.GetCurToken();
					}
					skipCheck_ = false;
					return res;
				}
				else
					throw Errors::SyntaxError(name.GetText() + " is not an struct");
				break;
			default:
				return new SynVal(name.GetText());
		}
		
		return res;
	}

	SynExpression *BasicParser::InternalParseExpression(Int2Type<lowest_level>)
	{
	    SynExpression *res = 0;
		Token token = tokenizer_.GetCurToken();
		TokenType tt = token.GetTokenType();
		switch(tt)
		{
			case ttOpenBrkt:
				tokenizer_.NextToken();
				res = ParseExpression();
				if(tokenizer_.GetCurToken().GetTokenType() != ttCloseBrkt)
					throw Errors::SyntaxError("Parenthesis mismatch");
				break;
			case ttInteger:
				res = new SynInt(token.GetIntValue()); break;
			case ttChar:
				res = new SynChar(token.GetCharValue()); break;
			case ttString:
				res = new SynString(token.GetStringValue()); break;
			case ttFloat:
				res = new SynFloat(token.GetFloatValue()); break;
			case ttIdentificator:
				return ParseIdentifier();
			default:
				throw Errors::SyntaxError("Expected constant or identifier");
		}
		tokenizer_.NextToken();
		return res;		
	}

	SynExpression *BasicParser::ParseExpression()
	{
		return InternalParseExpression(Int2Type<0>());
	}

	SynExpression *BasicParser2::ParseExpression2()
	{
		Token tk = tokenizer_.GetCurToken();
		if(strToTNT.count(tk.GetText()))
		{
			Token var = tokenizer_.NextToken();
			if(var.GetTokenType() == ttIdentificator)
				tinySymbolTable[var.GetText()] = strToTNT[tk.GetText()];
			else
				throw Errors::SyntaxError("Expected declaring name");
			tokenizer_.NextToken();
		}
		else if(tk.GetTokenType() != ttEof)
			return ParseExpression();
		else
			throw Errors::EndOfFile();
		return ParseExpression2();
		// 1. if declaration - look in the table
		// 2. else run ParseExpression()
		// also ParseExpression need to be modified
		//return InternalParseExpression(Int2Type<0>());
	}

	void SynBinaryOp::print(std::ostream &stream, int ident)
	{
		second_->print(stream, ident + 1);
		for(int i=0; i<ident; ++i) stream << '\t';
		stream << binOp_ << '\n';
		first_->print(stream, ident + 1);
	}

	void SynUnaryOp::print(std::ostream &stream, int ident)
	{
		if(postfix_)
			first_->print(stream, ident + 1);
		for(int i=0; i<ident; ++i) stream << '\t';
		stream << unOp_<< '\n';
		if(!postfix_)
			first_->print(stream, ident + 1);
	}

	void SynVal::print(std::ostream &stream, int ident)
	{
		for(int i=0; i<ident; ++i) stream << '\t';
		stream << identifier_ << '\n';
	}
	void SynInt::print(std::ostream &stream, int ident)
	{
		for(int i=0; i<ident; ++i) stream << '\t';
		stream << value_ << '\n';
	}
	void SynChar::print(std::ostream &stream, int ident)
	{
		for(int i=0; i<ident; ++i) stream << '\t';
		stream << value_ << '\n';
	}
	void SynFloat::print(std::ostream &stream, int ident)
	{
		for(int i=0; i<ident; ++i) stream << '\t';
		stream << value_ << '\n';
	}
	void SynString::print(std::ostream &stream, int ident)
	{
		for(int i=0; i<ident; ++i) stream << '\t';
		stream << value_ << '\n';
	}
	namespace ParserInit
	{
		Initializer::Initializer()
		{
			OpGroups[0].insert(ttPlus);
			OpGroups[0].insert(ttMinus);
			OpGroups[1].insert(ttAsterisk);
			OpGroups[1].insert(ttDiv);
			OpGroups[1].insert(ttMod);
			OpGroups[lowest_level].insert(ttInteger);
			OpGroups[lowest_level].insert(ttReal);
			OpGroups[lowest_level].insert(ttString);
			OpGroups[lowest_level].insert(ttChar);
			OpGroups[lowest_level].insert(ttIdentificator);
			strToTNT["int"] = ntVariable;
			strToTNT["double"] = ntVariable;
			strToTNT["void"] = ntFunction;
			strToTNT["struct"] = ntStruct;
			strToTNT["typedef"] = ntArray;
		}
	}
}